/**
*	@file RandomQueryGenerator.cpp
*
*	@brief this file random generate query and simple
*
*	@author li yuchen
*
*	@date 1/28/2011
*/
#include "RandomQueryGenerator.h"
#include <fstream>
#include <iostream>
#include <sstream>
#include <time.h>

// limit of inputs for each file
static int limit[8];
static bool limit_set = false;
static string var[] = {"x","y","z","w","a","b","c","d","e","i","j","k","m"}; // 13
static string proc[] = {"Pa","Pb","Pc","Pd","Pe","Pf"};

static string GenerateAssignCall(vector<string>& call_proc, int size);
static void GenerateFile(stringstream& ss, vector<string>& call_proc,int level);


void SetQueryLimit(int klm_c,int bambang,int lyc,int lzq,int wzy,int zn,int klm_pattern,int klm_with){
	limit[0] = klm_c;
	limit[1] = bambang;
	limit[2] = lyc;
	limit[3] = lzq;
	limit[4] = wzy;
	limit[5] = zn;
	limit[6] = klm_pattern;
	limit[7] = klm_with;
	limit_set = true;
}

void SetQueryLimitAll(int all_limit){
	for(int i = 0; i < 8; i++) limit[i] = all_limit;
	limit_set = true;
}

void RandomGenerateQuery(string input_file_dir, string output_file){
	if(limit_set == false) SetQueryLimitAll(1);
	srand(time(NULL));
	int num_query = rand() % 50 + 1;
	stringstream query_file;
	query_file << num_query << endl;
	query_file << "cat:spa_testcase" << endl;
	for(int i = 1; i <= num_query; i++){
		query_file << i << " - Name::" << endl;
		query_file << RandomGenerateSingleQuery(input_file_dir).c_str() << endl;
		query_file << endl;
		query_file << "5000" << endl;
	}

	ofstream write_file;
	write_file.open(output_file);
	write_file << query_file.str();
}

string RandomGenerateSingleQuery(string input_file_dir){
	stringstream query_file;

	string p[] = {"p1","p2","p3","p4"};
	string v[] = {"v1","v2","v3","v4"};
	string s[] = {"s1","s2","s3","s4"};
	string a[] = {"a1","a2","a3","a4"};
	string c[] = {"c1","c2","c3","c4"};
	string w[] = {"w1","w2","w3","w4"};
	string cons[] = {"t1","t2","t3","t4"};
	string brunch[] = {"i1","i2","i3","i4"};

	query_file << "procedure ";
	for(int i = 0; i < 3; i++) query_file << p[i].c_str() << ", ";
	query_file << p[3].c_str() <<"; ";

	query_file << "variable ";
	for(int i = 0; i < 3; i++) query_file << v[i].c_str() << ", ";
	query_file << v[3].c_str() <<"; ";

	query_file << "stmt ";
	for(int i = 0; i < 3; i++) query_file << s[i].c_str() << ", ";
	query_file << s[3].c_str() <<"; ";

	query_file << "assign ";
	for(int i = 0; i < 3; i++) query_file << a[i].c_str() << ", ";
	query_file << a[3].c_str() <<"; ";

	query_file << "call ";
	for(int i = 0; i < 3; i++) query_file << c[i].c_str() << ", ";
	query_file << c[3].c_str() <<"; ";

	query_file << "while ";
	for(int i = 0; i < 3; i++) query_file << w[i].c_str() << ", ";
	query_file << w[3].c_str() <<"; ";

	query_file << "constant ";
	for(int i = 0; i < 3; i++) query_file << cons[i].c_str() << ", ";
	query_file << cons[3].c_str() <<"; ";

	query_file << "if ";
	for(int i = 0; i < 3; i++) query_file << brunch[i].c_str() << ", ";
	query_file << brunch[3].c_str() <<"; ";

	query_file << endl;

	query_file << "Select ";
	
	if(rand()%2 == 0){
		query_file << "BOOLEAN ";
	}else{
		int num_of_var = rand() % 2 + 1; /** decision how many variables we output */
		query_file << "<";
		for(int i = 0; i < num_of_var; i++){
			switch(rand()%8){
			case 0:
				query_file << p[rand()%4].c_str();
				break;
			case 1:
				query_file << v[rand()%4].c_str();
				break;
			case 2:
				query_file << s[rand()%4].c_str();
				break;
			case 3:
				query_file << w[rand()%4].c_str();
				break;
			case 4:
				query_file << cons[rand()%4].c_str();
				break;
			case 5:
				query_file << brunch[rand()%4].c_str();
				break;
			case 6:
				query_file << a[rand()%4].c_str();
				break;
			case 7:
				query_file << c[rand()%4].c_str();
				break;
			}
			if(i != num_of_var - 1) query_file << ",";
			else query_file <<"> ";
		}
	}

	ifstream temp;
	string file[8];
	for(int i = 0; i < 8; i++) file[i] = input_file_dir;
	file[0].append("\\klm_c.txt");
	file[1].append("\\bambang.txt");
	file[2].append("\\lyc.txt");
	file[3].append("\\lzq.txt");
	file[4].append("\\wzy.txt");
	file[5].append("\\zn.txt");
	file[6].append("\\klm_pattern.txt");
	file[7].append("\\klm_with.txt");
	
	for(int i = 0; i < 8; i++){
		if(i == 0 && limit[0] + limit[1] + limit[2] + limit[3] + limit[4] + limit[5] != 0) query_file << "such that ";
		else if(i == 6 && limit[6] != 0) query_file << "pattern ";
		else if(i == 7 && limit[7] != 0) query_file << "with ";

		temp.open(file[i]);
		vector<string> query_box;
		while(temp.good()){
			string query_str = "";
			getline(temp,query_str);
			if(query_str.empty()) continue;
			query_box.push_back(query_str);
		}
		temp.close();
		for(int j = 0; j < limit[i]; j++){
			query_file << query_box[rand()%query_box.size()].c_str() << " ";
			if(j != limit[i] - 1){
				query_file << "and ";
			}
		}
		if(i < 5){
			int sum  = 0;
			for(int j = i + 1; j < 6; j++) sum += limit[j];
			if(sum != 0) query_file << "and ";
		}
	}

	return query_file.str();
}

string GenerateAssignCall(vector<string>& call_proc, int size){
	stringstream result;
	for(int i = 0; i < size; i++){
		if(rand() % size == 0 && call_proc.size() != 0){
			result << "call " << call_proc[rand()%call_proc.size()] << ";" << endl;
		}
		else{
			result << var[rand()%13] << " = ";
			int expr_size = (rand()%10+1);
			for(int i = 0; i < expr_size; i++){
				if(rand()%3 == 0) result << rand()%50;
				else result << var[rand()%13];
				if(i == expr_size - 1) result << ";" << endl;
				else{
					switch(rand()%3){
					case 0:result << "+";break;
					case 1:result << "*";break;
					case 2:result << "-"; break;
					}
				}
			}
			//result << "x = 1;" << endl;
		}
	}
	return result.str();
}

void GenerateFile(stringstream& ss, vector<string>& call_proc,int level){
	if(level == 0){
		ss << GenerateAssignCall(call_proc,2);
		return;
	}
	switch(rand()%3){
	case 0:
		ss << GenerateAssignCall(call_proc,2);
		if(rand()%2 == 0){
			ss << "while " << var[rand()%13] << "{" << endl;
			GenerateFile(ss,call_proc,level-1);
			ss << "}" << endl;
		}else{
			ss <<"if " << var[rand()%13] << " then{" << endl;
			GenerateFile(ss,call_proc,level-1);
			ss << "}else{" << endl;
			GenerateFile(ss,call_proc,level-1);
			ss << "}" << endl;
		}
		if(rand()%2 == 0){
			ss << "while " << var[rand()%13] << "{" << endl;
			GenerateFile(ss,call_proc,level-1);
			ss << "}" << endl;
		}else{
			ss <<"if " << var[rand()%13] << " then{" << endl;
			GenerateFile(ss,call_proc,level-1);
			ss << "}else{" << endl;
			GenerateFile(ss,call_proc,level-1);
			ss << "}" << endl;
		}
		return;
	case 1:
		if(rand()%2 == 0){
			ss << "while " << var[rand()%13] << "{" << endl;
			GenerateFile(ss,call_proc,level-1);
			ss << "}" << endl;
		}else{
			ss <<"if " << var[rand()%13] << " then{" << endl;
			GenerateFile(ss,call_proc,level-1);
			ss << "}else{" << endl;
			GenerateFile(ss,call_proc,level-1);
			ss << "}" << endl;
		}
		ss << GenerateAssignCall(call_proc,2);
		if(rand()%2 == 0){
			ss << "while " << var[rand()%13] << "{" << endl;
			GenerateFile(ss,call_proc,level-1);
			ss << "}" << endl;
		}else{
			ss <<"if " << var[rand()%13] << " then{" << endl;
			GenerateFile(ss,call_proc,level-1);
			ss << "}else{" << endl;
			GenerateFile(ss,call_proc,level-1);
			ss << "}" << endl;
		}
		return;
	case 2:
		if(rand()%2 == 0){
			ss << "while " << var[rand()%13] << "{" << endl;
			GenerateFile(ss,call_proc,level-1);
			ss << "}" << endl;
		}else{
			ss <<"if " << var[rand()%13] << " then{" << endl;
			GenerateFile(ss,call_proc,level-1);
			ss << "}else{" << endl;
			GenerateFile(ss,call_proc,level-1);
			ss << "}" << endl;
		}
		if(rand()%2 == 0){
			ss << "while " << var[rand()%13] << "{" << endl;
			GenerateFile(ss,call_proc,level-1);
			ss << "}" << endl;
		}else{
			ss <<"if " << var[rand()%13] << " then{" << endl;
			GenerateFile(ss,call_proc,level-1);
			ss << "}else{" << endl;
			GenerateFile(ss,call_proc,level-1);
			ss << "}" << endl;
		}
		ss << GenerateAssignCall(call_proc,2);
		return;
	}
}

void RandomGenerateSimple(string output_file, int how_many_proc, int nesting_level){
	ofstream file;
	file.open(output_file,ios::out);
	srand(time(NULL));
	stringstream ss;
	
	vector<string> call_proc;
	for(int i = 0; i < (how_many_proc > 6 ? 6 : how_many_proc); i++){
		call_proc.push_back(proc[i]);
	}
	int size = call_proc.size();
	for(int i = 0; i < size; i++){
		ss << "procedure " << call_proc[0] << "{" << endl;
		call_proc.erase(call_proc.begin());
		GenerateFile(ss,call_proc,nesting_level);
		ss << "}" << endl << endl;
		
	}
	file << ss.str();
	file.close();
}